package eventplanner.crawler;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
import com.gargoylesoftware.htmlunit.xml.XmlPage;
import eventplanner.ejb.dao.EventDao;
import eventplanner.entities.Event;
import eventplanner.entities.Feed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.CharacterData;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Schedules;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Stateless
public class RSSReader {

	private static final Logger LOG = LoggerFactory.getLogger(RSSReader.class);
	private static final Pattern descriptionPattern = Pattern.compile("<div><b>Omschrijving:</b>(.*?)</div>");
	private static final Pattern startDatePattern = Pattern.compile("<div><b>Start:</b>(.*?)</div>");
	private static final Pattern endDatePattern = Pattern.compile("<div><b>Einde:</b>(.*?)</div>");
	private static final Pattern linkPattern = Pattern.compile("<div><b>Link:</b>(.*?)</div>");
	private static final Pattern audiencePattern = Pattern.compile("<div><b>Doelgroep:</b>(.*?)</div>");

	private final DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
	private final DateFormat formatStartEnd = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.ENGLISH);
	private final WebClient webClient = new WebClient();

	private static final String username = "Gast1070@hu.nl";
	private static final String password = "javascript";

	@PersistenceContext
	private EntityManager em;

	@EJB
	private EventDao dao;

	@Schedules({
			@Schedule(hour = "6-19", minute = "*/30", dayOfWeek = "1-5"), // Every 30 minutes, between 6 am and 7 pm, for work days
			@Schedule(hour = "20-5", minute = "0", dayOfWeek = "1-5"), // Evey hour for work days outside the range of the above schedule
			@Schedule(hour = "*/6", minute = "0", dayOfWeek = "0,6") // Every 6 hours in the weekend
	})
	public void readFeeds() {

		List<Feed> feeds = em.createNamedQuery("feed_select_all", Feed.class).getResultList();

		DocumentBuilder builder;
		try {
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			LOG.error("The xml parser is misconfigured", e);
			return;
		}

		if (login()) {
			for (Feed feed : feeds) {
				List<Event> events = readFeed(builder, feed);
				for (Event event : events) {
					dao.persist(event);
					feed.addEvent(event);
				}
				LOG.info("Added {} events from the feed '{}'", events.size(), feed.getName());
			}
		}
	}

	public boolean login() {
		try {
			login(username, password);
		} catch (IOException e) {
			LOG.error("Unable to login to Sharepoint to download the feeds", e);
			return false;
		}
		return true;
	}

	public List<Event> readFeed(DocumentBuilder builder, Feed feed) {
		List<Event> events = new ArrayList<>();
		try {

			XmlPage xmlSite = webClient.getPage(feed.getUrl());
			String content = xmlSite.getContent().substring(1);
//			LOG.info("RSS: '{}'", content);
			Document doc = builder.parse(new InputSource(new StringReader(content)));

			NodeList nodes = doc.getElementsByTagName("item");
			for (int x = 0; x < nodes.getLength(); x++) {
				Element element = (Element) nodes.item(x);
				Event event = new Event(getElementValue(element, "title"),
						getDescriptionMatcher(element, descriptionPattern),
						getElementValue(element, "author"),
						getElementValue(element, "link"),
						format.parse(getElementValue(element, "pubDate")));

				logFeed(element);
				event.setStart(formatStartEnd.parse(getDescriptionMatcher(element, startDatePattern)));
				event.setEnd(formatStartEnd.parse(getDescriptionMatcher(element, endDatePattern)));
				event.setUrl(getDescriptionMatcher(element, linkPattern));
				event.setAudience(getDescriptionMatcher(element, audiencePattern));

				events.add(event);
			}

		} catch (IOException ex) {
			LOG.error("Unable to read the feed from '{}'", feed.getUrl(), ex);
		} catch (SAXException | ParseException e) {
			LOG.error("An error occured during reading of the feed", e);
		}
		return events;
	}

	private void logFeed(Element element) {
		LOG.debug("Title: " + getElementValue(element, "title"));
		LOG.debug("Link: " + getElementValue(element, "link"));
		LOG.debug("Publish Date: " + getElementValue(element, "pubDate"));
		LOG.debug("Author: " + getElementValue(element, "author"));
		LOG.debug("Omschrijving: " + getDescriptionMatcher(element, descriptionPattern));
		LOG.debug("Start: " + getDescriptionMatcher(element, startDatePattern));
		LOG.debug("Eind: " + getDescriptionMatcher(element, endDatePattern));
		LOG.debug("Link: " + getDescriptionMatcher(element, linkPattern));
		LOG.debug("Doelgroep: " + getDescriptionMatcher(element, audiencePattern));
	}

	private String getCharacterDataFromElement(Element e) {
		Node child = e.getFirstChild();
		if (child instanceof CharacterData) {
			CharacterData cd = (CharacterData) child;
			return cd.getData();
		}
		return "";
	}

	private String getElementValue(Element parent, String label) {
		Element element = (Element) parent.getElementsByTagName(label).item(0);
		return getCharacterDataFromElement(element);
	}

	private String getDescriptionMatcher(Element parent, Pattern pattern) {
		Matcher matcher = pattern.matcher(getElementValue(parent, "description"));
		if (matcher.find()) {
			return matcher.group(1);
		} else {
			return null;
		}
	}

	private void login(String username, String pass) throws FailingHttpStatusCodeException, IOException {
		HtmlPage inlogPage = webClient.getPage("https://intranet.sharepoint.hu.nl/FE/_layouts/listfeed.aspx?List={FC92FAFF-A136-4E14-9021-3133DA04AB4F}");

		HtmlSubmitInput button = inlogPage.getElementByName("SubmitCreds");
		HtmlTextInput usernameElement = inlogPage.getElementByName("username");
		HtmlPasswordInput passwordElement = inlogPage.getElementByName("password");

		usernameElement.setValueAttribute(username);
		passwordElement.setValueAttribute(pass);

		button.click();

		webClient.closeAllWindows();
	}
}